#include "wl_socket.h"
#include "wl_trace.h"
#include "wl_transport.h"
#include "sockets.h"
#include "ql_data_call.h"

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus*/

typedef struct __wl_socket_global {
    wl_uint32 simid;
    wl_uint32 pdpid;
    wl_uint32 state;
} wl_socket_global;

static wl_socket_global s_socket_global;

wl_int32 wl_socket_set_network(wl_uint32 simid, wl_uint32 pdpid, wl_uint32 state) {
    s_socket_global.simid = simid;
    s_socket_global.pdpid = pdpid;
    s_socket_global.state = state;
    return 0;
}

wl_int32 wl_socket_get_network(OUT wl_uint32* simid, OUT wl_uint32* pdpid, OUT wl_uint32* state) {
    *simid = s_socket_global.simid;
    *pdpid = s_socket_global.pdpid;
    *state = s_socket_global.state;
    return 0;
}

WL_SOCKET wl_socket_create(wl_int32 domain, wl_int32 type, wl_int32 protocol, wl_int32 taskid) {
    WL_SOCKET_DESCRIPTOR sd   = WL_INVALID_SOCKET;
    WL_SOCKET            sock = WL_INVALID_SOCKET;
    wl_uint32            mode = 0;
    int sock_nbio = 1;
    sd = socket(domain, type, protocol);

    ioctl(sd, FIONBIO, &sock_nbio);

    mode = 2048;
    if (0 > setsockopt(sd, SOL_SOCKET, SO_RCVBUF, (wl_char*)&mode, sizeof(wl_uint32))) {
        WLLOGW("sci_sock_setsockopt SO_RCVBUF fail");
        close(sd);
        return WL_INVALID_SOCKET;
    }

    if (WL_INVALID_SOCKET == (sock = wl_transport_add_socket(sd, type, taskid))) {
        close(sd);
        WLLOGW("wl_transport_add_socket sd %d fail", sd);
        return WL_INVALID_SOCKET;
    }

    return sock;
}

wl_int32 wl_socket_bind(WL_SOCKET socket, const wl_sockaddr* addr, wl_int32 addrlen) {
    wl_int32             status = 0;
    WL_SOCKET_DESCRIPTOR sd     = WL_INVALID_SOCKET;

    if (WL_INVALID_SOCKET == (sd = wl_transport_get_socket_descriptor(socket))) {
        WLLOGW("socket %d not exist", socket);
        return -1;
    }

    status = bind(sd, (const struct sockaddr*)addr, addrlen);
    if (status < 0) {
        WLLOGW("bind socket %d fail", socket);
        return -1;
    }

    return 0;
}

wl_int32 wl_socket_set_callback(WL_SOCKET socket, wl_sock_callback callback, void* context) {
    return wl_transport_set_socket_callback(socket, callback, context);
}

wl_int32 wl_socket_sendto(WL_SOCKET          socket,
                          const void*        data,
                          wl_int32           datalen,
                          wl_uint32          flags,
                          const wl_sockaddr* addr,
                          wl_int32           addrlen) {
    wl_int32             sendlen = 0;
    WL_SOCKET_DESCRIPTOR sd      = WL_INVALID_SOCKET;

    if (WL_INVALID_SOCKET == (sd = wl_transport_get_socket_descriptor(socket))) {
        WLLOGW("socket %d not exist", socket);
        return 0;
    }

    sendlen = sendto(sd, data, datalen, flags, (struct sockaddr*)addr, addrlen);

    if (0 > sendlen) {
        wl_int32 error = errno;
        if (EINTR != error && EWOULDBLOCK != error && EAGAIN != error) {
            WLLOGW("sendto socket %d error %d", socket, error);
            return 0;
        }

        wl_transport_set_socket_event(socket, WL_TRANSPORT_EVENT_WRITE);
        return -1;
    } else if (0 == sendlen) {
        WLLOGW("sendto socket %d zero", socket);
        return 0;
    } else {
        wl_transport_set_socket_event(socket, WL_TRANSPORT_EVENT_READ);
    }

    return sendlen;
}

wl_int32 wl_socket_recvfrom(WL_SOCKET socket,
                            OUT void* data,
                            wl_int32  datalen,
                            wl_uint32 flags,
                            OUT wl_sockaddr* addr,
                            OUT wl_uint32* addrlen) {
    WL_SOCKET_DESCRIPTOR sd      = WL_INVALID_SOCKET;
    wl_int32             recvlen = 0;

    if (WL_INVALID_SOCKET == (sd = wl_transport_get_socket_descriptor(socket))) {
        WLLOGW("socket %d not exist", socket);
        return 0;
    }

    recvlen = recvfrom(sd, data, datalen, flags, (struct sockaddr*)addr, addrlen);

    if (0 > recvlen) {
        int error = errno;
        if (EINTR != error && EWOULDBLOCK != error && EAGAIN != error) {
            WLLOGW("recvfrom socket %d error %d", socket, error);
            return 0;
        }
        return -1;
    } else if (0 == recvlen) {
        WLLOGW("recvfrom socket %d zero", socket);
        return 0;
    }
    wl_transport_set_socket_event(socket, WL_TRANSPORT_EVENT_READ);
    return recvlen;
}

wl_int32 wl_socket_connect(WL_SOCKET socket, const wl_sockaddr* addr, wl_int32 addrlen) {
    WL_SOCKET_DESCRIPTOR sd = WL_INVALID_SOCKET;
    struct ql_data_call_info info = {0};

    if (WL_INVALID_SOCKET == (sd = wl_transport_get_socket_descriptor(socket))) {
        WLLOGW("socket %d not exist", socket);
        return -1;
    }
        
    if (0 == ql_get_data_call_info(1, 0, &info)) {
        struct sockaddr_in ip4_local_addr = {0};
        struct sockaddr_in conn_addr;
        ip4_local_addr.sin_family = AF_INET;
        ip4_local_addr.sin_port = htons(ql_soc_generate_port());
        ip4_local_addr.sin_addr = info.v4.addr.ip;
        if (bind(sd, (struct sockaddr*)&(ip4_local_addr), sizeof(ip4_local_addr)) < 0) {
            return -1;
        }

        conn_addr.sin_addr.s_addr = ((wl_sockaddr_in *)addr)->sin_addr.s_addr;
        conn_addr.sin_family = ((wl_sockaddr_in *)addr)->sin_family;
        conn_addr.sin_port = ((wl_sockaddr_in *)addr)->sin_port;
        conn_addr.sin_len = sizeof(struct sockaddr_in);
        if (connect(sd, (struct sockaddr *)&conn_addr, sizeof(struct sockaddr)) < 0) {
            int error = errno;
            if (EINPROGRESS != error) {
                WLLOGW("connect socket %d error %d", socket, error);
                return -1;
            }else {
                wl_transport_set_socket_event(socket, WL_TRANSPORT_EVENT_CONNECT);
            }
        }

        return 0;
    }

    return -1;
}

wl_int32 wl_socket_send(WL_SOCKET socket, void* data, wl_int32 datalen, wl_uint32 flags) {
    wl_int32             sendlen = 0;
    WL_SOCKET_DESCRIPTOR sd      = WL_INVALID_SOCKET;

    if (WL_INVALID_SOCKET == (sd = wl_transport_get_socket_descriptor(socket))) {
        WLLOGW("socket %d not exist", socket);
        return 0;
    }

    sendlen = send(sd, data, datalen, flags);

    if (0 > sendlen) {
        wl_int32 error = errno;
        if (EINTR != error && EWOULDBLOCK != error && EAGAIN != error) {
            WLLOGW("send socket %d error %d", socket, error);
            return 0;
        }else if (error == EWOULDBLOCK || error == EAGAIN) {
            wl_transport_set_socket_event(socket, WL_TRANSPORT_EVENT_WRITE);
        }

        return -1;
    } else if (0 == sendlen) {

    } else if (sendlen < datalen) {
        wl_transport_set_socket_event(socket, WL_TRANSPORT_EVENT_WRITE);
    } else {
        wl_transport_set_socket_event(socket, WL_TRANSPORT_EVENT_READ);
    }

    return sendlen;
}

wl_int32 wl_socket_recv(WL_SOCKET socket, OUT void* data, wl_int32 datalen, wl_uint32 flags) {
    WL_SOCKET_DESCRIPTOR sd      = WL_INVALID_SOCKET;
    wl_int32             recvlen = 0;

    if (WL_INVALID_SOCKET == (sd = wl_transport_get_socket_descriptor(socket))) {
        WLLOGW("socket %d not exist", socket);
        return 0;
    }

    recvlen = recv(sd, data, datalen, flags);

    if (0 > recvlen) {
        int error = errno;
        if (EINTR != error && EWOULDBLOCK != error && EAGAIN != error) {
            WLLOGW("recv socket %d error %d", socket, error);
            return 0;
        }else if (error == EWOULDBLOCK || error == EAGAIN) {
            wl_transport_set_socket_event(socket, WL_TRANSPORT_EVENT_READ);
            WLLOGW("recv -1, error:%d", error);
        }

        return -1;
    }

   
    return recvlen;
}

wl_int32 wl_socket_listen(WL_SOCKET socket, wl_int32 backlog) {
    WL_SOCKET_DESCRIPTOR sd = WL_INVALID_SOCKET;

    if (WL_INVALID_SOCKET == (sd = wl_transport_get_socket_descriptor(socket))) {
        WLLOGW("socket %d not exist", socket);
        return -1;
    }

    if (listen(sd, backlog) < 0) {
        WLLOGW("listen socket %d fail", socket);
        return -1;
    }

    wl_transport_set_socket_event(socket, WL_TRANSPORT_EVENT_ACCEPT);
    return 0;
}

WL_SOCKET wl_socket_accept(WL_SOCKET socket, OUT wl_sockaddr* addr, OUT wl_int32* addrlen) {
    wl_uint32            mode       = 0;
    WL_SOCKET_DESCRIPTOR new_sd     = WL_INVALID_SOCKET;
    WL_SOCKET            new_socket = WL_INVALID_SOCKET;

    if (WL_INVALID_SOCKET == wl_transport_get_socket_descriptor(socket)) {
        WLLOGW("socket %d not exist", socket);
        return -1;
    }

    new_sd = accept(socket, (struct sockaddr*)addr, (socklen_t*)addrlen);
    if (0 > new_sd) {
        WLLOGW("accept socket %d fail", socket);
        return WL_INVALID_SOCKET;
    }
    if (ioctl(new_sd, FIONBIO, &mode) < 0) {
        close(new_sd);
        return WL_INVALID_SOCKET;
    }

    if (WL_INVALID_SOCKET
        == (new_socket = wl_transport_add_socket(new_sd, WL_SOCK_STREAM, wl_transport_get_socket_taskid(socket)))) {
        WLLOGW("wl_transport_add_socket socket %d fail", new_sd);
        close(new_sd);
        return WL_INVALID_SOCKET;
    }
    wl_transport_set_socket_event(socket, WL_TRANSPORT_EVENT_READ);
    wl_transport_set_socket_event(new_socket, WL_TRANSPORT_EVENT_READ);
    return new_socket;
}

wl_int32 wl_socket_close(WL_SOCKET socket) {
    WL_SOCKET_DESCRIPTOR sd = WL_INVALID_SOCKET;
    if (WL_INVALID_SOCKET == (sd = wl_transport_get_socket_descriptor(socket))) {
        WLLOGW("socket %d not exist", socket);
        return -1;
    }
    wl_transport_delete_socket(socket);
    //把关闭socket的工作交由tranport的select线程处理
    //close(sd);
    return 0;
}

wl_int32 wl_socket_ntohl(wl_int32 net) {
    return ntohl(net);
}

wl_int16 wl_socket_ntohs(wl_int16 net) {
    return ntohs(net);
}

wl_int32 wl_socket_htonl(wl_int32 host) {
    return htonl(host);
}

wl_int16 wl_socket_htons(wl_int16 host) {
    return htons(host);
}

wl_int32 wl_socket_inet_ntoa(wl_in_addr* in_addr, OUT wl_char* ip) {
    struct in_addr addr;
    addr.s_addr = in_addr->s_addr;
    strcpy(ip, inet_ntoa(addr));
    return 0;
}

wl_in_addr wl_socket_inet_addr(wl_char* ip) {
    wl_in_addr in_addr;
    in_addr.s_addr = (wl_uint32)inet_addr(ip);
    return in_addr;
}

#ifdef __cplusplus
}
#endif /* __cplusplus */
